package com.qingxin.sys.utils;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.util.Assert;

/**
 * bean工具类
 */
public class BeanUtil extends PropertyUtils {
	
	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
	
	
	/**
	 * 直接读取对象属性值
	 * 
	 * @param object
	 *            要读取的对象
	 * @param fieldName
	 *            要读取的属性名
	 * @return 要读取的属性值
	 */
	public static Object getFieldValue(final Object object, final String fieldName) {
		Field field = getDeclaredField(object, fieldName);
		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		makeAccessible(field);

		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("never happend exception!", e);
		}
		return result;
	}

	
	/**
	 * 直接设置对象属性值,无视private/protected修饰符,不经过getter函数.
	 * 
	 * @param object
	 *            要设置的对象
	 * @param fieldName
	 *            要设置的属性名
	 * @return 要设置的属性值
	 */
	public static void setFieldValue(final Object object, final String fieldName, final Object value) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		makeAccessible(field);

		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("never happend exception!", e);
		}
	}

	
	/**
	 * 循环向上转型,获取对象中某个属性的DeclaredField.
	 * 
	 * @param clazz
	 *            待获取DeclaredField的对象
	 * @param fieldName
	 *            要获取DeclaredField的属性名
	 * @return fieldName的DeclaredField
	 */
	protected static Field getDeclaredField(final Object object, final String fieldName) {
		Assert.notNull(object);
		return getDeclaredField(object.getClass(), fieldName);
	}

	
	/**
	 * 循环向上转型,获取类中某个属性的DeclaredField.
	 * 
	 * @param clazz
	 *            待获取DeclaredField的类
	 * @param fieldName
	 *            要获取DeclaredField的属性名
	 * @return fieldName的DeclaredField
	 */
	@SuppressWarnings("rawtypes")
	protected static Field getDeclaredField(final Class clazz, final String fieldName) {
		Assert.notNull(clazz);
		Assert.hasText(fieldName);
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(fieldName);
			} catch (NoSuchFieldException e) {
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	
	/**
	 * 强制转换fileld可访问.
	 * 
	 * @param field
	 *            待强制转换的fileld名称
	 */
	protected static void makeAccessible(final Field field) {
		if (!Modifier.isPublic(field.getModifiers())
				|| !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	
	/**
	 * 设置bean中某个属性值
	 * 
	 * @param bean
	 * @param propName
	 * @param value
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	public static void setSimpleProperty(Object bean, String propName, Object value)
			throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		bean.getClass().getMethod(getWriteMethod(propName)).invoke(bean, value);
	}

	
	/**
	 * 得到对应属性的set方法名称
	 * 
	 * @param name
	 * @return
	 */
	private static String getWriteMethod(String name) {
		return "set" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
	}
	
	
	/**
	 * 获取指定字段的数据类型
	 * 
	 * @param clazz
	 * @param fieldName
	 * @return
	 */
	public static Class<?> getFieldType(final Class<?> clazz, final String fieldName) {
		Field field = getDeclaredField(clazz, fieldName);
		return field.getType();
	}

	
	/**
	 * 获取内容发生变化的字段内容<br>
	 * 比较newObj和oldObj中指定字段的值是否发生了变化，如果发生了变化则返回该变化描述，否则忽略<br>
	 * <br>
	 * 其中newObj和oldObj必须为同一数据类型或包含有相同的字段信息
	 * 
	 * @param oldObj 原始对象
	 * @param newObj 修改后的对象
	 * @param fields 字段列表，用半角分号或逗号分隔
	 * @param isInclude 如果为true则fields为需要比较的字段，如果为false则fields为需要排除的字段
	 * @return
	 */
	public static String getChangedFields(Object oldObj, Object newObj, String fields, boolean isInclude){
		return getChangedFields(oldObj, newObj, fields, isInclude, null);
	}
	
	
	/**
	 * 获取内容发生变化的字段内容<br>
	 * 比较newObj和oldObj中指定字段的值是否发生了变化，如果发生了变化则返回该变化描述，否则忽略<br>
	 * <br>
	 * 其中newObj和oldObj必须为同一数据类型或包含有相同的字段信息
	 * 
	 * @param oldObj 原始对象
	 * @param newObj 修改后的对象
	 * @param fields 字段列表，用半角分号或逗号分隔
	 * @param isInclude 如果为true则fields为需要比较的字段，如果为false则fields为需要排除的字段
	 * @param dateFormat 日期类型字段格式配置，格式为< field, format>,如< crtime, 'yyyy-MM-dd'>,如果不指定格式，将按毫秒精度进行比较
	 * @return
	 */
	public static String getChangedFields(Object oldObj, Object newObj, String fields, boolean isInclude, Map<String, String> dateFormat){
		String[] arr = fields.split("[,;]");
		StringBuilder buf = new StringBuilder();
		try {
			if(isInclude){//包含
				for(String field : arr){
					if(StringUtils.isEmpty(field)){
						continue;
					}
					Object oldValue = getFieldValue(oldObj, field);
					Object newValue = getFieldValue(newObj, field);
					if(oldValue==null){
						if(newValue != null){
							if(newValue instanceof Date){
								buf.append("[").append(field).append(": 由‘null’改为‘").append(DATE_FORMAT.format((Date)newValue)).append("’]");
							}else{
								buf.append("[").append(field).append(": 由‘null’改为‘").append(newValue).append("’]");
							}
						}
					}else{
						if(oldValue instanceof Date){
							buf.append(compareDateField(field, (Date)oldValue, (Date)newValue, dateFormat));
						}else if(!oldValue.equals(newValue)){
							buf.append("[").append(field).append(": 由‘").append(oldValue).append("’改为‘").append(newValue).append("’]");
						}
					} 
				}
			}else{//排除
				Set<String> exclude = new HashSet<String>();
				Collections.addAll(exclude, arr);
				Field[] fs = oldObj.getClass().getDeclaredFields();//所有字段
				AccessibleObject.setAccessible(fs, true);
				for(Field f : fs){
					Class<?> ct = f.getType();
					if(Collection.class.isAssignableFrom(ct) || Map.class.isAssignableFrom(ct) || ct.isArray()){
						continue;
					}
					String field = f.getName();
					if(exclude.contains(field)){
						continue;
					}
					Object oldValue = f.get(oldObj);
					Object newValue = getFieldValue(newObj, field);
					if(oldValue == null){
						if(newValue != null){
							if(newValue instanceof Date){
								buf.append("[").append(field).append(": 由‘null’改为‘").append(DATE_FORMAT.format((Date)newValue)).append("’]");
							}else{
								buf.append("[").append(field).append(": 由‘null’改为‘").append(newValue).append("’]");
							}
						}
					}else{
						if(oldValue instanceof Date){
							buf.append(compareDateField(field, (Date)oldValue, (Date)newValue, dateFormat));
						}else if(!oldValue.equals(newValue)){
							buf.append("[").append(field).append(": 由‘").append(oldValue).append("’改为‘").append(newValue).append("’]");
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buf.toString();
	}
	//比较日期字段
	private static String compareDateField(String field, Date oldValue, Date newValue, Map<String, String> dateFormat){
		StringBuilder buf = new StringBuilder();
		if(newValue == null){
			buf.append("[").append(field).append(": 由‘").append(DATE_FORMAT.format((Date)oldValue)).append("’改为‘null’]");
		}else{
			if(dateFormat == null || dateFormat.get(field)==null){
				if(!oldValue.equals(newValue)){
					buf.append("[").append(field).append(": 由‘").append(DATE_FORMAT.format((Date)oldValue))
							.append("’改为‘").append(DATE_FORMAT.format((Date)newValue)).append("’]");
				}
			}else{
				SimpleDateFormat format = new SimpleDateFormat(dateFormat.get(field));
				String oldv = format.format(oldValue);
				String newv = format.format(newValue);
				if(!oldv.equals(newv)){
					buf.append("[").append(field).append(": 由‘").append(oldv).append("’改为‘").append(newv).append("’]");
				}
			}
		}
		return buf.toString();
	}
	
	
	/**
	 * 将source中指定的字段设置到target字段内容<br>
	 * <br>
	 * 其中source和target必须为同一数据类型或包含有相同的字段信息
	 * 
	 * @param target 目标对象
	 * @param source 原始对象
	 * @param fields 字段列表，用半角分号或逗号分隔
	 * @param isInclude 如果为true则fields为需要设置的字段，如果为false则fields为需要排除的字段
	 */
	public static void setTargetFields(Object target, Object source, String fields, boolean isInclude){
		String[] arr = fields.split("[,;]");
		try {
			if(isInclude){//包含
				for(String field : arr){
					if(StringUtils.isEmpty(field)){
						continue;
					}
					Object value = getProperty(source, field);
					setProperty(target, field, value);
				}
			}else{//排除
				Set<String> exclude = new HashSet<String>();
				Collections.addAll(exclude, arr);
				Field[] fs = source.getClass().getDeclaredFields();//所有字段
				for(Field f : fs){
					Class<?> ct = f.getType();
					if(Collection.class.isAssignableFrom(ct) || Map.class.isAssignableFrom(ct) || ct.isArray()){
						continue;
					}
					String field = f.getName();
					if(exclude.contains(field)){
						continue;
					}
					Object value = getProperty(source, field);
					setProperty(target, field, value);
				}
				
			}
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	
	
	
	
	
}
